/* Gannet OS - VGA Functions for Kernel
 
 This file contains the VGA functions for the kernel, with primitive functions
 to allow development and debugging of the kernel to the output port. It only
 supports VGA Text mode at the moment.

 This is the C Code file (see vga.h for the header)

 This is part of the Gannet OS, For copy right and licensing see LICENSE in the
 root folder, or visit http://gannet.org.uk/license/
*/

#include "vga.h"

// FIXME: JT: I don't like these yet, can we confirm if they're needed or recode?
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

// magic numbers to where the Video memory is
static const uint8_t _VGA_WIDTH = 80;
static const uint8_t _VGA_HEIGHT = 24;
static const size_t _VGA_MEMORY_START = 0xB8000; 
static const size_t _VGA_MEMORY_MAX = 0xB8F00;

size_t _vga_current_row;
size_t _vga_current_column;

uint16_t* _vga_position;


uint8_t _vga_current_forecolour;
uint8_t _vga_current_backcolour;

bool flashing;

char _vga_current_colour;

char _vga_make_color(enum vga_color fg, enum vga_color bg) {
	// Perhaps this should check that BG is in range and not overflowing.
	char code = fg | bg << 4;
	
	if(flashing) {
		code = code | (0x01 << 7); // set first bit to 1.
	} else {
		code = code & 0x7F; // set first bit to 0
	}
	return code;
}

uint16_t _vga_make_vgachar(char c, char color) {
	uint16_t c16 = (uint16_t) c;
	uint16_t color16 = (uint16_t) color;
	return c16 | color16 << 8;
}

// FIXME: JT: I'm not happy with either this function or its use.
size_t _vga_strlen(const char* str) {
	size_t ret = 0;
	while ( str[ret] != 0 )
		ret++;
	return ret;
}

void vga_initialise() {
	_vga_current_row = 0;
	_vga_current_column = 0;
	_vga_current_colour = _vga_make_color(COLOUR_LIGHT_GREY, COLOUR_BLACK);
	
	_vga_position = (uint16_t*) _VGA_MEMORY_START;
	
	uint16_t space = _vga_make_vgachar(' ', _vga_current_colour);
	
	while(_vga_position <= (uint16_t*) _VGA_MEMORY_MAX) {
		*_vga_position = space;
		_vga_position = _vga_position + 1;
	}
	
	_vga_position = (uint16_t*) _VGA_MEMORY_START;
}

void vga_log(const char* data) {
	// FIXME: only loop once (move the pointer!)
	size_t datalen = _vga_strlen(data);
	for ( size_t i = 0; i < datalen; i++ ) {
		*_vga_position = _vga_make_vgachar(data[i], _vga_current_colour);
		_vga_position = _vga_position +1;
		
		// Loop back to the beginning if we've gone off the end
		if(_vga_position >= (uint16_t*) _VGA_MEMORY_MAX){
			_vga_position = (uint16_t*) _VGA_MEMORY_START;
		}
	}
}

